package com.kfyang.manualgear.daoplugin.reference;

import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.project.Project;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.util.ProcessingContext;
import com.kfyang.manualgear.daoplugin.Constants;
import com.kfyang.manualgear.daoplugin.util.PsiUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.psi.YAMLDocument;
import org.jetbrains.yaml.psi.YAMLKeyValue;
import org.jetbrains.yaml.psi.YAMLMapping;
import org.jetbrains.yaml.psi.YAMLPsiElement;
import org.jetbrains.yaml.psi.impl.YAMLPlainTextImpl;
import com.intellij.openapi.util.TextRange;

import java.util.Arrays;
import java.util.List;

import static com.kfyang.manualgear.daoplugin.util.PsiUtils.inManualGear;

public class YamlConstructReferenceContributor  extends PsiReferenceContributor {
    @Override
    public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
        registrar.registerReferenceProvider(PlatformPatterns.psiElement(YAMLPlainTextImpl.class), new PsiReferenceProvider() {
            private static boolean isConstruct(PsiElement element) {
                return element.getParent() instanceof YAMLKeyValue keyValue
                    && keyValue.getKeyText().equals("construct")
                    && keyValue.getParent() instanceof YAMLMapping yamlMapping
                    && yamlMapping.getParent() instanceof YAMLDocument;
            }

            private static String getMethodName(PsiElement element) {
                YAMLDocument yamlDocument = (YAMLDocument) PsiUtils.findParent(element, 3);
                // 找映射
                YAMLMapping yamlMapping = null;
                for (YAMLPsiElement psiElement : yamlDocument.getYAMLElements()) {
                    if (psiElement instanceof YAMLMapping tempMapping) {
                        yamlMapping = tempMapping;
                        break;
                    }
                }
                if (yamlMapping == null) {
                    return null;
                }
                return yamlMapping.getKeyValueByKey("methodName").getValueText();
            }

            private static PsiMethod findMethod(PsiElement element) {
                String methodName = getMethodName(element);
                if (methodName == null) {
                    return null;
                }

                PsiFile topLevelFile = InjectedLanguageManager.getInstance(element.getProject()).getTopLevelFile(element);
                String yamlFileName = topLevelFile.getName();
                String className = yamlFileName.substring(0, yamlFileName.lastIndexOf('.'));

                PsiFile[] classFiles = PsiShortNamesCache.getInstance(element.getProject())
                    .getFilesByName(className + ".java");
                if (classFiles.length != 1) {
                    return null;
                }
                PsiJavaFile psiJavaFile = (PsiJavaFile) classFiles[0];
                List<PsiClass> psiClasses = Arrays.stream(psiJavaFile.getClasses())
                    .filter(psiClass -> psiClass.getName().equals(className))
                    .toList();
                if (psiClasses.size() != 1) {
                    return null;
                }
                PsiClass psiClass = psiClasses.get(0);
                if (!psiClass.isInterface()) {
                    return null;
                }
                PsiMethod[] methods = psiClass.findMethodsByName(methodName, false);
                if (methods.length != 1) {
                    return null;
                }
                return methods[0];
            }

            @Override
            public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext processingContext) {
                if (!isConstruct(element) || !inManualGear(element)) {
                    return new PsiReference[0];
                }

                PsiMethod method = findMethod(element);
                if (method == null) {
                    return new PsiReference[0];
                }

                Project project = element.getProject();
                PsiTypeElement returnTypeElement = method.getReturnTypeElement();


                if (returnTypeElement == null) {
                    return new PsiReference[0];
                }
                String className;
                if (returnTypeElement.getText().startsWith("List<") || returnTypeElement.getText().startsWith("PageResult<")) {
                    String canonicalText = returnTypeElement.getType().getCanonicalText();
                    className = canonicalText.substring(canonicalText.indexOf('<') + 1, canonicalText.length() - 1);
                } else {
                    className = returnTypeElement.getType().getCanonicalText();
                }

                // 找entity中的构造器
                PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
                if (psiClass == null) {
                    return new PsiReference[0];
                }
                PsiLiteralExpression toReference = null;
                for (PsiMethod constructor : psiClass.getConstructors()) {
                    if (constructor.hasAnnotation(Constants.CONSTRUCT_ANNO)) {
                        PsiAnnotation constructorAnno = constructor.getAnnotation(Constants.CONSTRUCT_ANNO);
                        PsiLiteralExpression tempReference = (PsiLiteralExpression) constructorAnno.findAttributeValue("value");
                        if (tempReference != null && tempReference.getText().equals("\"" + element.getText() + "\"")) {
                            toReference = tempReference;
                            break;
                        }
                    }
                }
                if (toReference == null) return new PsiReference[0];
                return new PsiReference[]{new ConstructReference(element, new TextRange(0, element.getText().length()), toReference)};
            }
        });
    }
}
